package boc.sx.gateway.socket.client;

import boc.sx.gateway.socket.client.support.InitializerFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author Andon
 * 2022/7/22
 * <p>
 * Netty客户端
 */
@Slf4j
@Component
@Scope("prototype")
public class NettyClient{

    public static Map<String,CountDownLatch> awaitThreadMap = new ConcurrentHashMap<>();

    public static Map<String,Object> receiveMessage = new ConcurrentHashMap<>();

    private final EventLoopGroup workGroup = new NioEventLoopGroup();

    private String host;

    private Integer port;

    private String requestCode;

    private ChannelInitializer initializer;

    public void config(String host,int port,String requestCode){
        this.host = host;
        this.port = port;
        this.requestCode = requestCode;
    }
    public void setClientProtocol(String protocol,String requestCode,Object msg,String path){
        this.initializer = InitializerFactory.getInitializer(protocol,requestCode,msg,path);
    }

    public void setClientProtocol(ChannelInitializer initializer){
        this.initializer = initializer;
    }

    @PreDestroy
    private void destroy() {
        workGroup.shutdownGracefully();
        log.warn("Netty连接关闭!!");
    }

    public Object run() throws InterruptedException {
        if(host == null || port ==null){
            log.error("客户端未配置地址或断口号");
            return null;
        }
//        String requestCode = host+port+path+"-"+msg.hashCode();
//        NettyClientHandler clientHandler = new NettyClientHandler(path,requestCode,msg);
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(workGroup)
                    .channel(NioSocketChannel.class)
                    // 设置TCP长连接，TCP会主动探测空闲连接的有效性
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    // 禁用Nagle算法，小数据时可以即时传输
                    .option(ChannelOption.TCP_NODELAY, true)
                    // 发送缓冲区大小
                    .option(ChannelOption.SO_SNDBUF, 256 * 1024)
                    // 接收缓冲区大小
                    .option(ChannelOption.SO_RCVBUF, 256 * 1024)
                    .handler(initializer);
                    // Netty客户端channel初始化
//                    .handler(new ChannelInitializer<SocketChannel>() {
//                        protected void initChannel(SocketChannel channel) throws Exception {
//                            //包含编码器和解码器
//                            channel.pipeline().addLast(new HttpClientCodec());
//                            //聚合
//                            channel.pipeline().addLast(new HttpObjectAggregator(1024 * 10 * 1024));
//                            //解压
//                            channel.pipeline().addLast(new HttpContentDecompressor());
//                            channel.pipeline().addLast(clientHandler);
//                        }
//                    });
            // 连接服务器ip、端口
            ChannelFuture future = bootstrap.connect(host, port).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("连接Netty服务端异常!! error:{}", e.getMessage());
            return null;
        }
        finally {
            workGroup.shutdownGracefully();
        }
//        doWait(requestCode);
        String res = (String)getMsg(requestCode);
        return res;
    }

    public static Object getMsg(String code){
        Object res = receiveMessage.get(code);
        receiveMessage.remove(code);
        return res;
    }

    public void doWait(String requestCode) throws InterruptedException {
        if(!receiveMessage.containsKey(requestCode)){
            CountDownLatch latch = new CountDownLatch(1);
            awaitThreadMap.put(requestCode,latch);
            latch.await(5,TimeUnit.SECONDS);
            awaitThreadMap.remove(requestCode);
        }
    }

    public static void notifyThread(String code,Object msg){
//        CountDownLatch countDownLatch = awaitThreadMap.get(code);
        receiveMessage.put(code,msg);
//        if(countDownLatch != null){
//            countDownLatch.countDown();
//        }
    }

    public static void clean(String code){
        receiveMessage.remove(code);
        awaitThreadMap.remove(code);
    }
}
